/*
   Copyright 2009 Abdulla Abdurakhmanov (abdulla@latestbit.com)
   Original sources are available at www.latestbit.com

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

package org.latestbit.smartobjects;

import java.nio.ByteBuffer;

import java.util.Locale;
import java.util.TimeZone;

import org.latestbit.smartobjects.coders.IDataTypesCoder;
import org.latestbit.smartobjects.core.ServerSkeleton;
import org.latestbit.smartobjects.encrypters.IDataEncrypter;
import org.latestbit.smartobjects.exceptions.SmartBusSystemException;
import org.latestbit.smartobjects.io.ResizableByteBuffer;
import org.latestbit.smartobjects.network.ConnectionHandler;

/**
 * RequestContext provides access to some additional system information for 
 * current request in client or server side available in the current context (thread).
 * The information contains: remote side locale, timezone, timeout for operation processing, 
 * some custom additional user data, etc.
 * At server side (skeleton implementation), RequestContext for client call can be obtained 
 * with calling ServerSkeleton.getCurrentRequestContext(), at client side 
 * all generated client stubs include additional optional parameter (requestContext). 
 * If request context is not specified manually in client side, the library uses default 
 * request context from IRemoteSmartObject instance.
 * @author abdulla-a
 *
 */
public class RequestContext implements ISerializableObject {
	public static final int DEFAULT_COMMAND_OPERATION_TIMEOUT = 30;
	
	private int maxResponseTimeoutInSec = DEFAULT_COMMAND_OPERATION_TIMEOUT;
	private static Locale DEFAULT_LOCALE = Locale.getDefault();
	private Locale locale = DEFAULT_LOCALE;
	private static TimeZone DEFAULT_TIMEZONE = TimeZone.getDefault();
	private TimeZone timezone = DEFAULT_TIMEZONE;
	
	private byte[] userContextBuffer; 

	private ISmartBus bus;
	private ServerSkeleton skeleton;
	private ConnectionHandler connectionHandler;
	
	
	@Override
	public String getObjectType() {
		return "org.latestbit.smartobjects.RequestContext";
	}
	
	/**
	 * Sets max response/reply timeout for request
	 * @param maxResponseTimeoutInSec Timeout in seconds
	 */
	public void setMaxResponseTimeoutInSec(int maxResponseTimeoutInSec) {
		this.maxResponseTimeoutInSec = maxResponseTimeoutInSec;
	}

	/**
	 * Gets max response/reply timeout for request
	 * @return Timeout in seconds
	 */
	public int getMaxResponseTimeoutInSec() {
		return maxResponseTimeoutInSec;
	}

	/**
	 * Sets locale for request
	 * @param locale Locale
	 */
	public void setLocale(Locale locale) {
		this.locale = locale;
	}

	/**
	 * Gets locale for request
	 * @return locale 
	 */
	public Locale getLocale() {
		return locale;
	}
	
	/**
	 * Sets timezone for request
	 * @param timezone Timezone
	 */
	public void setTimezone(TimeZone timezone) {
		this.timezone = timezone;
	}

	/**
	 * Gets timezone for request
	 * @return Timezone
	 */
	public TimeZone getTimezone() {
		return timezone;
	}

	/**
	 * Sets custom application defined serialized data
	 * @param userContextBuffer user defined data
	 */
	public void setUserContextBuffer(byte[] userContextBuffer) {
		this.userContextBuffer = userContextBuffer;
	}

	/**
	 * Gets custom application defined serialized data
	 * @return user defined data
	 */
	public byte[] getUserContextBuffer() {
		return userContextBuffer;
	}
	
	public void setConnectionHandler(ConnectionHandler connectionHandler) {
		this.connectionHandler = connectionHandler;
	}

	/**
	 * Gets connection handler where is request is come from
	 * @return connection handler
	 */
	public ConnectionHandler getConnectionHandler() {
		return connectionHandler;
	}
	
	public void setBus(ISmartBus bus) {
		this.bus = bus;
	}

	public ISmartBus getBus() {
		return bus;
	}
	
	public void setSkeleton(ServerSkeleton skeleton) {
		this.skeleton = skeleton;
	}

	/**
	 * Gets skeleton where is request come from
	 * @return skeleton instance
	 */
	public ServerSkeleton getSkeleton() {
		return skeleton;
	}	

	@Override
	public void deserialize(IDataTypesCoder coder, IDataEncrypter encrypter, ByteBuffer readBuffer)	throws SmartBusSystemException {
		this.maxResponseTimeoutInSec = coder.decodeInt32 ( readBuffer );
		
		String localeAsStr = coder.decodeString(readBuffer);
		String language= null, country = null;
		if ( localeAsStr!=null && localeAsStr.contains("_")) {
			String[] localeArr = localeAsStr.split ( "_" );
			if(localeArr.length > 0)
				language = localeArr[0];
			if(localeArr.length > 1)
				country = localeArr[1];
		}
		else
			language = localeAsStr;
		
		if( country == null )
			this.locale = new Locale ( language );
		else
		if( language != null )
			this.locale = new Locale ( language, country );
		else
			this.locale = null;
		
		String tzId = coder.decodeString(readBuffer);
		if ( tzId!=null )
			this.timezone = TimeZone.getTimeZone(tzId);
		else
			this.timezone = null;
		
		if(!coder.decodeNull(readBuffer)) {
			setUserContextBuffer(coder.decodeByteArray(readBuffer));
		}
		else
			setUserContextBuffer(null);
	}

	@Override
	public void serialize(IDataTypesCoder coder, IDataEncrypter encrypter, ResizableByteBuffer writeBuffer)	throws SmartBusSystemException {
		
		coder.encodeInt32(this.maxResponseTimeoutInSec, writeBuffer );
		if(this.locale!=null)
			coder.encodeString(this.locale.getLanguage() + "_" + this.locale.getCountry(), writeBuffer);
		else {
			coder.encodeNull(writeBuffer);
		}

		if(this.timezone!=null)
			coder.encodeString(this.timezone.getID(), writeBuffer);
		else
			coder.encodeNull(writeBuffer);
		
		if ( this.userContextBuffer!=null )
			coder.encodeByteArray(this.userContextBuffer, writeBuffer);
		else
			coder.encodeNull(writeBuffer);
	}


}
